// 基础

// javascript 是区分大小写的 并使用 Unicode 字符集 举个例子 可以将单词 Früh（在德语中意思是“早”）用作变量名
// 举例：const Früh = "foobar";
// 由于 javascript 是大小写敏感的 因此变量 früh 和 Früh 则是两个不同的变量
// 在 javascript 中 指令被称为语句 并用分号 ; 进行分隔


// 注释
// 单行注释
/**
 * 多
 * 行
 * 注
 * 释
 */
let b1 = 1; /* 多行注释可以用在单行吗？答：可以 */
let b2 /* number */ = 1; /* 多行注释还可以写在语句内部 */

/**
 * 问：注释可以嵌套吗？
 * 答：不可以 √
 */

//* 声明
/**
 * javascript 有三种变量声明方式：
 * 1.var 声明一个变量 可选择将其初始化为一个值
 * 2.let 声明一个块级作用域的局部变量 可选择将其初始化为一个值
 * 3.const 声明一个块级作用域的只读命名常量
 */

/**
 * 问：const 声明的变量，可以修改它的值吗？
 * 答：不可以 √
 */

/**变量
 * 在应用程序中 将变量用作值的符号名 变量的名字又叫做标识符 其需要遵守一定的规则
 * javascript 标识符通常以字母、下划线（_）或者美元符号（$）开头
 * 后续的字符也可以是数字（0-9）
 * 因为 javascript 是区分大小写的，所以字母包含从 A 到 Z 的大写字母和从 a 到 z 的小写字母
 *
 * 合法的标识符示例：Number_hits、temp99、$credit 和 _name
 */

/**
 * 声明变量
 * 可以用以下两种方式声明变量：
 * var 例如 var x = 42 这个语法可以用来声明局部变量和全局变量 具体取决于执行上下文
 * const 或 let  例如 let y = 13 这个语法可以用来声明块级作用域的局部变量
 *
 * 可以使用解构赋值语法声明用于解包值的变量
 * 例如 const { bar } = foo 这会创建一个名为 bar 的变量 并且将 foo 对象中属性名与之相同的属性的值赋给它\
 * javascript 过去允许给未声明的变量赋值 而这会创建一个**未声明的全局**变量 这在严格模式下是一个错误 应该彻底避免使用它
 */

/**
 * 问：JavaScript变量的命名规则是？
 * 1. 所有Unicode字符集中的字符
 * 2. 所有的字母、数字
 * 3. _ $
 * 4. 不能以数字开头
 * 5. 区分大小写
 * 6. 不能与关键字重名
 *
 * 问：const { bar } = foo 这是一种怎样的赋值语法？
 * 1. 解构是指解构对象
 * 2. 解构，就是将对象中的属性直接拿出来用
 * const { bar } = { bar: 1 };
 * const { length } = [14, 123, 12]
 *
 * const fn3 = () => ({bar: 1}); // 没有大括号的箭头函数，在返回对象时，需要将对象用()包裹住。
 * ({bar: 1}).bar; // 单独用对象直接访问对象的值时，也需要用()包裹
 * const { bar } = fn3();
 *
 * // 箭头函数简写
 * const fn1 = (a, b) => {
 *     return a + b;
 * }
 * const fn2 = (a, b) => a + b; // 没有大括号的箭头函数，箭头后面就是函数的返回值表达式。
 */

/**
 * 声明和初始化
 * 在 let x = 42 这样的语句中 let x 称作声明 “= 42”称作初始化器
 * 声明允许在后续的代码中访问变量时不会抛出 ReferenceError 而初始化器会给变量赋值
 * 在 var 和 let 声明中 初始化器是可选的 如果声明变量时没有进行初始化 变量会被赋值为 undefined
 * 示例：
 * let x;
 * console.log(x); // 输出“undefined”
 * 本质上 let x = 42 等价于 let x; x = 42
 * const 声明总是需要初始化器 因为它们禁止在声明后进行任何类型的赋值 以及隐式地将其初始化为 undefined
 */

/**
 * 问：下面这行代码为什么会报错？
 * const y;
 * 答：不允许使用无初始值设定项的 'const' 变量
 *
 * 问：下面两行代码能同时存在吗？
 * let x;
 * let x = 42;
 * 答：不能 √
 * let x;
 * x = 42;
 *
 * 问：下面代码中，那部分时变量的初始化器？
 * let x = 55;
 * function add(a, b = 5) { return a + b } // 函数中的参数，可以通过初始化器，来赋予参数（入参）默认值
 * add(1) // 如果参数存在默认值，则可以忽略其传参
 */

/**变量作用域
 * 一个变量可能属于下列作用域之一：
 * 全局作用域：在脚本模式中运行的所有代码的默认作用域
 * 模块作用域：在模块模式中运行的代码的作用域
 * 函数作用域：由函数创建的作用域
 *
 * 此外 用 let 或 const 声明的变量属于另一个作用域：
 * 块级作用域：用一对花括号创建的作用域（块）
 * 当在函数的外部声明变量时 该变量被称作全局变量 因为当前文档中任何其他代码都能使用它
 * 当在函数内声明变量时 该变量被称作局部变量 因为它仅在那个函数内可用
 * let 和 const 声明也会被限制在声明所在的块语句中
 * 示例：
 * if (Math.random() > 0.5) {
 *   const y = 5;
 * }
 * console.log(y); // 输出 ReferenceError: y is not defined
 * 然而用 var 创建的变量不是块级作用域的 而只是块所在的*函数*的
 * 例如下列代码会输出 5 因为 x 的作用域是全局上下文（如果代码是函数的一部分的话 就是函数上下文） x 的作用域不受附近的 if 语句块的限制
 * 示例：
 * if (true) {
 *   var x = 5;
 * }
 * console.log(x); // x 是 5
 *
 * 问：常见的作用域有哪些？
 * 1. 全局作用域（在模块模式下，等同于：模块作用域）
 * 2. 函数作用域
 * 3. 块作用域
 */

// 浏览器 运行环境
let x1 = 123; // 全局作用域
// node 运行环境
let x2 = 123; // 模块作用域（浏览器构建模式下，也存在模块作用域）

/**
 * 变量提升
 * var 声明的变量会被提升 如果你在声明变量之前访问该变量 其值总是 undefined
 * 因为只有该变量的声明和默认初始化（为 undefined）被提升 而不是它的值赋值
 * 示例：
 * console.log(x === undefined); // true
 * var x = 3;
 *
 * (function () {
 *   console.log(x); // undefined
 *   var x = "局部值";
 *
 *   console.log(x);
 * })()
 * 上面的例子可以被解释为：
 * var x;
 * console.log(x === undefined); // true
 * x = 3;
 *
 * (function () {
 *   var x;
 *   console.log(x); // undefined
 *   x = "局部值";
 * })();
 *
 * let 和 const 是否被提升是个定义争论
 * 在变量声明之前引用块中的变量 将总是抛出 ReferenceError 因为该变量位于从块的开始到声明所在的
 * “暂时性死区”中
 * 示例：
 * console.log(x); // ReferenceError
 * const x = 3;
 *
 * console.log(y); // ReferenceError
 * let y = 3;
 * 与 var 声明不同（其仅提升声明，不提升其值），函数声明是全部被提升
 *
 * 问：let 和 const 存在变量提升问题吗？
 * 答：不存在 √
 */

/**
 * 全局变量
 * 全局变量是全局对象的属性
 * 在网页中 全局对象是 window 所以你可以用 window.variable 语法读取和设置全局变量
 * 你可以通过指定 window 或 frame 的名字 在当前 window 或 frame 访问另一个 window 或 frame 中声明的变量
 * 例如： 如果在文档中声明了一个叫 phoneNumber 的变量 那么你就可以在 iframe 中使用 parent.phoneNumber 引用它
 *
 * 全局作用域是顶层作用域，有且只有一个
 *
 * 问：浏览器全局作用域的变量名是？ window - 温斗
 * 问：node全局作用域的变量名是？ global - 哥咯bo
 */


/**常量
 * 你可以用 const 关键字创建一个只读命名常量
 * 常量标识符的语法和任何变量标识符的语法相同：必须以字母、下划线或美元符号（$）开头并可以包含字母、数字或下划线
 * const PI = 3.14;
 * 常量不可以通过赋值来改变其值或在脚本运行时被重新声明
 * 必须为其初始化一个值 常量的作用域规则和 let 块级作用域变量的一致
 *
 * 在同一作用域中 不能使用与变量名或函数名相同的名字来声明常量
 * 示例：
 * // 这会造成错误
 * function f() {}
 * const f = 5;
 *
 * // 这也会造成错误
 * function f() {
 *   const g = 5;
 *   var g;
 * }
 *
 * 然而 const 仅阻止重新赋值 而不阻止修改
 * 被赋值为常量的对象的属性是不受保护的 所以下面的语句执行时不会产生错误
 * 示例：
 * const MY_OBJECT = { key: "值" };
 * MY_OBJECT.key = "其他值";
 *
 * 同样的 数组的元素也是不受保护的 所以下面的语句执行时也不会产生错误
 * 示例：
 * const a = ["HTML", "CSS"];
 * a.push("JAVASCRIPT");//添加
 * console.log(a); // ['HTML', 'CSS', 'JAVASCRIPT'];
 *
 * 问：数组常量、对象常量的值是否可以被修改？
 * const a = 1;
 * a = 2; // 不可以
 * const b = '123123';
 * b = '????'; // 不可以
 * const c = false;
 * c = true; // 不可以
 *
 * const d = { bar: 1 };
 * d = { bar: 2 } // 不可以
 * d.bar = 2; // 对象常量的值可以修改
 * delete d.bar; // 对象常量的可以删除
 *
 * const e = [1, 2, 4];
 * e.push(3); // 数组常量的值可以修改
 * e.splice(0, 1, 1); // 数组常量的值可以删除
 * 答：可以
 *
 * 问：函数常量是否可以被修改呢？
 * const fn = () => {};
 * fn = () => {}; // 可以吗？
 * 答：不可以。
 */


// 【数据结构和类型】
/**
 * 数据类型
 * 最新的 ECMAScript 标准定义了 8 种数据类型：
 *
 * 七种基本数据类型：
 * 大写的，就是Class名，可以用 new [Class名](传参);
 * 小写的，就是类型名
 * 3种基本类型
 * Boolean boolean true 和 false
 * Number number  整数或浮点数  例如：42 或 3.14159
 * String string 表示文本值的字符序列  例如："Howdy"
 *
 * 2种空类型
 * null 是一个特殊的 Object object 一个表示空值的特殊关键字  （因为 javascript 是区分大小写的 所以 null 和 Null、NULL 或其他变体是不一样的）
 * undefined 没有对象 一个未定义值的顶级属性
 *
 * 对象类型和函数类型
 * 以及 Object object
 * 和 Function function
 *
 * let x = 1;
 * let x = new Number(1);
 *
 * typeof new Boolean('1') === 'boolean';
 * typeof new Number('1') === 'number';
 * typeof new String('abc') === 'string';
 * typeof new Object({ bar: 1 }) === 'object';
 * typeof new Function() === 'function';
 *
 * 2种特殊类型，这2种Class不需要 new，当成函数用
 * BigInt bigint  任意精度的整数  例如：9007199254740992n
 * Symbol symbol 其实例是唯一且不可变的数据类型
 * 比如：BigInt(123); Symbol('123');
 *
 * 问：所有的变量、字面量都是什么？
 * 答：都是对象
 *
 * 问：在字面量中，哪一种自变量不能通过.直接使用数字类的函数？
 * 答：数字字面量，但是可以通过 [] 来访问数字类的函数
 * 例如：
 * let x = 1.111111;
 * x.toFixed(2) // '1.11'
 * 1.1111111['toFixed'](2) // '1.11'
 */

/**数据类型的转换
 * javascript 是一门动态类型语言
 * 这意味着你在声明变量时可以不必指定该变量的数据类型 这也意味着在脚本执行期间会根据需要自动转换数据类型
 * 因此 可以按照如下方式来定义变量：
 * let answer = 42;
 * 然后 还可以给同一个变量赋予一个字符串值 例如：
 * answer = "不客气";
 * 因为 javascript 是动态类型的 这种赋值方式并不会提示出错
 */

/**数字和“+”运算符
 * 在使用 + 运算符的表达式中涉及数字和字符串 javascript 会把数字转换成字符串
 * 例如：
 * x = "答案是 " + 42; // "答案是 42"
 * y = 42 + " 是答案"; // "42 是答案"
 * z = "37" + 7; // "377"
 *
 * 在使用其他运算符时 javascript 不会把数字转换成字符串
 * 例如：
 * x = "37" - 7; // 30
 * y = "37" * 7; // 259
 */

/**
 * 字符串转换为数字
 * 有一些方法可以将内存中表示一个数字的字符串转换为数字: parseInt() 和 parseFloat()
 * parseInt 只返回整数 因此它在处理小数时用途有限
 * 备注：另外 parseInt 的最佳实践是总是带上进制参数 这个参数用于指定使用哪一种进制
 * 示例：parseInt("101", 2); // 5
 *
 * 将字符串转换为数字的另一种方法是使用 +（一元加）运算符
 * 示例：
 * "1.1" + "1.1" = "1.11.1"
 * +"1.1" + +"1.1" = 2.2
 *
 * parseInt('123', 进制数); // 进制数最小是2进制，最高支持36进制
 */


// 【字面量】
/**
 * 布尔字面量
 * 布尔类型有两种字面量值：true 和 false
 */

/**
 * 数字字面量
 * javascript 数字字面量包括多种基数的整数字面量和以 10 为基数的浮点数字面量
 * 语言规范要求数字字面量必须是无符号的
 * 但是像 -123.4 这样的代码片段还是没有问题的 会被解释为一元操作符 - 应用于数字字面量 123.4
 *
 * 【整数字面量】
 * 整数和 BigInt 字面量可以用十进制（基数 10）、十六进制（基数 16）、八进制（基数 8）和二进制（基数 2）表示
 *
 * 【浮点数字面量】
 * 浮点数字面量可以有以下的组成部分：
 * 一个无符号的十进制整数
 * 小数点（“.”）
 * 小数部分（另一个十进制数）
 * 指数部分
 */

/**
 * 字符串字面量
 * 字符串字面量是由一对双引号（"）或单引号（'）括起来的零个或多个字符 字符串被限定在同种引号之间（也即，必须是成对单引号或成对双引号）
 * " // 双引号
 * ' // 单引号
 * ` // 反引号 esc 下面的键
 */

/**
 * 对象字面量
 * 对象字面量是由一对花括号（{}）括起来的包含零个或多个属性名和相关值的列表
 */

/**
 * 数组字面量
 * 数组字面量是由一对方括号（[]）括起来的包含零个或多个表达式的列表 其中每个表达式代表一个数组元素
 * 其 length 被设定为指定的参数的个数
 * 下面的示例创建了含有 3 个元素的 coffees 数组 它的长度是 3
 * 示例：
 * const coffees = ["French Roast", "Colombian", "Kona"];
 * 每次字面量被求值时 数组字面量都会创建一个新的数组对象
 * 例如： 在全局作用域中用字面量定义的数组在脚本加载后被创建
 * 然而 如果数组字面量位于函数内 每次调用函数时会初始化一个新数组
 */

// 通过初始化器，来给x5变量赋值一个数组
// 声明了x5变量，并赋值一个数组
let x5 = [1, 2, 3, 4];

/**
 * 数组字面量中的多余逗号
 * 如果你在数组字面量中连续放置两个逗号 数组会为未指定的元素留下一个空槽
 * 示例：
 * const fish = ["Lion", , "Angel"];
 * 打印这个数组时会看见：
 * console.log(fish);// [ 'Lion', <1 empty item>, 'Angel' ]
 *
 * 第二项是“empty” 与实际的 undefined 值完全不同
 * 当使用数组遍历方法（例如 Array.prototype.map）时 空槽会被跳过
 * 然而索引访问 fish[1] 仍会返回 undefined
 *
 * 在下面的例子中 数组的 length 是 3 并不存在 myList[3] 这个元素 列表中所有其他的逗号都表示一个新元素
 * 示例：
 * const myList = ["home", , "school"];
 * 在下面的例子中 数组的 length 是4 ，元素 myList[0] 和 myList[2] 缺失
 * 示例：
 * const myList = [, "home", , "school"];
 * 再看一个例子 在这里 该数组的 length 是 4，元素 myList[1] 和 myList[3] 缺失 “只有最后的那个逗号被忽略”
 * 示例：const myList = ["home", , "school", ,];
 *
 * 问：数组长度与数组索引值最大值的关系是？
 * 答：数组索引值 = 数组长度 - 1
 */

/**
 * 转义字符
 * 通过在引号前加上反斜杠 可以在字符串中插入引号 这就是引号转义
 * 例如：
 * const quote = "He read \"The Cremation of Sam McGee\" by R.W. Service.";
 * console.log(quote);
 * 代码的运行结果为：
 * He read "The Cremation of Sam McGee" by R.W. Service.
 *
 * \n
 * \r
 * \s
 * %o
 */

let a = 1;
let b = 2;
console.log(` ${a} + ${b} = ${a + b}`);